home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
The World of Computer Software.iso
/
vsc92nov.zip
/
storage.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-11-02
|
19KB
|
860 lines
/*
* storage.c -- Implementation of a storage allocation scheme with
* stop-and-copy-based garbage collection
*
* (C) m.b (Matthias Blume), Wed Jun 3 15:12:45 MET DST 1992, HUB/Ger
* Humboldt-University of Berlin, Germany
*/
# ident "@(#)storage.c (C) M.Blume, Humboldt-Uni Berlin, 1.9"
# include "align.h"
# include "storage.h"
# include "storage-t.h"
# include "except.h"
# include <stdio.h>
# include <stdlib.h>
# include <memory.h>
# include <assert.h>
# include <string.h>
# define GVA_SIZE_INCR 32 /* propably never needed more than once */
# define NULL_IDENTIFIER 253
# define MARK_IDENTIFIER 254
# define REFERTO_IDENTIFIER 255
struct broken_heart {
object_descriptor _;
void *new_location;
};
/* fake object description for broken hearts */
static
struct object_description broken_heart_description = {
0,
sizeof (struct broken_heart),
NULL,
NULL,
0,
&broken_heart_description,
NULL, NULL, NULL,
NULL, NULL, NULL,
NULL
};
/*
* Determining the type of an object
*/
# ifdef SCMTYPEOF_FUNC
object_descriptor ScmTypeOf (void *obj)
{
return obj == NULL ? NULL : ((struct broken_heart *)obj)->_;
}
# endif
/* indicator of running gc */
static
int gc_running = 0;
/* rubber band array of global variable's locations: */
static
size_t gva_length, gva_firstfree;
static
struct gva {
void *location;
perform_on_subs_proc for_each_sub;
} *gva = NULL;
int register_global_object (void *location, perform_on_subs_proc proc)
{
if (gva == NULL) {
if ((gva = (struct gva *) malloc (sizeof (struct gva) * GVA_SIZE_INCR))
== NULL)
return -1;
gva_length = GVA_SIZE_INCR;
gva_firstfree = 0;
} else if (gva_firstfree >= gva_length) {
struct gva *tmp;
if ((tmp = (struct gva *)
realloc (gva, sizeof (struct gva)
* (GVA_SIZE_INCR + gva_length)))
== NULL)
return -1;
gva = tmp;
gva_length += GVA_SIZE_INCR;
}
gva [gva_firstfree].location = location;
gva [gva_firstfree].for_each_sub = proc;
gva_firstfree++;
return 0;
}
struct heap_block {
size_t size; /* total length of array */
size_t firstfree; /* first free position in array */
size_t next_copied; /* used during gc */
align_t array[1]; /* itself */
};
struct heap {
size_t size; /* total number of heap block entries */
size_t firstfree; /* number of allocated heap blocks */
size_t search_start; /* start search for free space here */
size_t next_copied; /* used during gc */
struct heap_block **array;
size_t total_size;
size_t total_used;
};
static
struct heap heap = {
0,
0,
0,
0,
NULL,
0,
0
};
static struct heap passive_heap = {
0,
0,
0,
0,
NULL,
0,
0
};
static size_t min_heap_size = MIN_HEAP_SIZE;
static
size_t index_of_satisfying_heap_block (size_t elem)
{
size_t i;
for (i = heap.search_start; i < heap.firstfree; i++)
if (elem <= heap.array[i]->size - heap.array[i]->firstfree)
break;
return i;
}
static
size_t index_of_new_heap_block (size_t elem)
{
size_t size;
struct heap_block *block;
struct heap_block **tmp;
# ifdef DEBUG
fprintf (stderr, "DEBUG: allocating new heap block (elem = %d)\n", elem);
# endif
size = (elem > HEAP_BLOCK_SIZE) ? elem : HEAP_BLOCK_SIZE;
if (heap.array == NULL) {
if ((heap.array = (struct heap_block **)
malloc (HEAP_SIZE_INCR * sizeof (struct heap_block *)))
== NULL)
return (size_t) -1;
heap.size = HEAP_SIZE_INCR;
heap.firstfree = heap.search_start = 0;
} else if (heap.firstfree >= heap.size) {
if ((tmp = (struct heap_block **)
realloc (heap.array,
(heap.size + HEAP_SIZE_INCR) * sizeof (struct heap_block *)))
== NULL)
return (size_t) -1;
heap.array = tmp;
heap.size += HEAP_SIZE_INCR;
}
if ((block = (struct heap_block *)
malloc (sizeof (struct heap_block) +
sizeof (align_t) * (size - 1)))
== NULL)
return (size_t) -1;
heap.total_size += size;
block->size = size;
block->firstfree = block->next_copied = 0;
heap.array [heap.firstfree] = block;
return heap.firstfree++;
}
/* forward declaration of gc_getbytes */
static
void *gc_getbytes (size_t bytes);
/*ARGSUSED*/
static
void update_address (void **location, void *ignore)
{
struct broken_heart *obj;
void *new_location;
size_t size;
size_hook_proc shp;
if ((obj = (struct broken_heart *) *location) == NULL) {
return;
}
if (obj->_ == &broken_heart_description) {
*location = obj->new_location;
} else {
if ((size = obj->_->size) == 0) {
if ((shp = obj->_->size_hook) == NULL) /* constant non-heap object */
return; /* don't move it ! */
else
size = (* shp) (obj);
}
/* relies on having gc_running set!!! */
new_location = gc_getbytes (size);
memcpy (new_location, (void *) obj, size);
obj->new_location = *location = new_location;
obj->_ = &broken_heart_description;
}
}
static
int cmp_heap_block_size (const void *vx1, const void *vx2)
{
size_t s1, s2;
s1 = (*(struct heap_block **)vx1)->size;
s2 = (*(struct heap_block **)vx2)->size;
if (s1 < s2)
return -1;
if (s1 == s2)
return 0;
return 1;
}
static
void *next_moved_object (void)
{
struct heap_block *hb = NULL;
struct broken_heart *res;
size_t size, i;
while (heap.next_copied < heap.search_start &&
heap.array [heap.next_copied]->next_copied >=
heap.array [heap.next_copied]->firstfree)
heap.next_copied++;
for (i = heap.next_copied; i < heap.firstfree; i++) {
hb = heap.array [i];
if (hb->next_copied < hb->firstfree)
break;
}
if (i == heap.firstfree)
return NULL;
res = (void *) (hb->array + hb->next_copied);
if ((size = res->_->size) == 0)
size = (* res->_->size_hook) (res);
hb->next_copied += (size + sizeof (align_t) - 1) / sizeof (align_t);
return res;
}
# define PRE_GC 1
# define POST_GC 2
# define MODULE_INIT 3
static
void all_modules (int what)
{
unsigned i;
module_proc mip;
for (i = 0; i < identifier_map_length; i++) {
switch (what) {
case PRE_GC:
mip = identifier_map[i]->pre_gc_action;
break;
case POST_GC:
mip = identifier_map[i]->post_gc_action;
break;
default: /* MODULE_INIT */
mip = identifier_map[i]->module_init;
break;
}
if (mip != NULL)
(* mip) ();
}
}
static
void sort (void *array, size_t len, size_t siz,
int (* cmp) (const void *, const void *))
{
qsort (array, len, siz, cmp);
}
static align_t dummy_cache_buf [1];
static align_t *cached_heap_top = dummy_cache_buf;
static align_t *cached_heap_end = dummy_cache_buf;
static size_t cached_block_no = 0;
static
void exchange_heaps (void)
{
struct heap tmp;
size_t i;
/* swap heaps */
tmp = passive_heap;
passive_heap = heap;
heap = tmp;
/* Sort entries of new heap */
if (heap.array != NULL && heap.firstfree > 0)
sort (heap.array,
heap.firstfree,
sizeof (struct heap_block *),
cmp_heap_block_size);
/* reset all firstfree- and next_copied-members */
for (i = 0; i < heap.firstfree; i++)
heap.array[i]->firstfree =
heap.array[i]->next_copied = 0;
heap.search_start = heap.next_copied = heap.total_used = 0;
/* reset cache */
cached_heap_top = cached_heap_end = dummy_cache_buf;
}
static size_t getbytes_count = 0;
static clock_t gc_clock_accu = 0;
clock_t total_gc_clock (void)
{
return gc_clock_accu;
}
static
void garbage_collection (void)
{
size_t i;
struct broken_heart *next;
perform_on_subs_proc fesp;
size_t pre_gc_gbc = getbytes_count;
clock_t tmp_clock;
# ifdef DEBUG
fputs ("DEBUG: Collecting ...", stderr);
# endif
/* mark gc as being running */
announce_gc_start ();
gc_running = 1;
tmp_clock = clock ();
all_modules (PRE_GC);
exchange_heaps ();
/* run update_address on global locations */
for (i = 0; i < gva_firstfree; i++)
if ((fesp = gva [i].for_each_sub) != NULL)
(* fesp) (gva [i].location, update_address, NULL);
else
update_address (gva [i].location, NULL);
/* just do it! */
while (next = next_moved_object ())
if ((fesp = next->_->for_each_sub) != NULL)
(* fesp) (next, update_address, NULL);
all_modules (POST_GC);
tmp_clock = clock () - tmp_clock;
gc_clock_accu += tmp_clock;
/* reset gc_running flag */
gc_running = 0;
announce_gc_end ();
gc_statistics_proc (
pre_gc_gbc, getbytes_count - pre_gc_gbc,
min_heap_size, heap.total_size, heap.total_used,
tmp_clock);
getbytes_count = 0;
# ifdef DEBUG
fputs (" done\n", stderr);
# endif
}
void gc_set_min_heap_size (size_t bound)
{
min_heap_size = bound;
}
static
void *gc_getbytes (size_t bytes)
{
size_t elem = (bytes + sizeof (align_t) - 1) / sizeof (align_t);
size_t i;
struct heap_block *hb;
void *ret;
getbytes_count++;
if ((i = index_of_satisfying_heap_block (elem)) >= heap.firstfree) {
i = index_of_new_heap_block (elem);
if (i == (size_t) -1)
fatal ("Out of heap");
}
heap.total_used += elem;
hb = heap.array[i];
ret = (void *) (hb->array + hb->firstfree);
hb->firstfree += elem;
if (i != heap.search_start &&
hb->size - hb->firstfree < SIZE_MINIMUM) {
heap.array [i] = heap.array [heap.search_start];
heap.array [heap.search_start++] = hb;
} else if (hb->size - hb->firstfree < SIZE_MINIMUM)
heap.search_start++;
return ret;
}
static void uncache (void)
{
struct heap_block *hb;
size_t i;
if (cached_heap_end != dummy_cache_buf) {
hb = heap.array [cached_block_no];
i = hb->firstfree;
hb->firstfree = cached_heap_top - hb->array;
heap.total_used += hb->firstfree - i;
if (cached_block_no != heap.search_start &&
hb->size - hb->firstfree < SIZE_MINIMUM) {
heap.array [cached_block_no] = heap.array [heap.search_start];
heap.array [heap.search_start++] = hb;
} else if (hb->size - hb->firstfree < SIZE_MINIMUM)
heap.search_start++;
}
}
static
void *getbytes (size_t bytes)
{
size_t elem = (bytes + sizeof (align_t) - 1) / sizeof (align_t);
size_t i;
struct heap_block *hb;
void *ret;
getbytes_count++;
ret = cached_heap_top;
cached_heap_top += elem;
if (cached_heap_top <= cached_heap_end)
return ret;
cached_heap_top -= elem;
uncache ();
if ((i = index_of_satisfying_heap_block (elem)) >= heap.firstfree)
if (gc_running || heap.total_size < min_heap_size) {
i = index_of_new_heap_block (elem);
if (i == (size_t) -1) {
if (gc_running)
fatal ("Out of heap");
else {
/* decrease min_heap_size to avoid further warnings */
min_heap_size /= 2;
warning ("memory allocation problem causes premature GC");
goto the_normal_way;
}
}
} else {
the_normal_way:
garbage_collection ();
uncache (); /* necessary due to with-gc-strategy */
if ((i = index_of_satisfying_heap_block (elem)) >= heap.firstfree)
i = index_of_new_heap_block (elem);
if (i == (size_t) -1)
reset ("Out of heap");
}
cached_block_no = i;
hb = heap.array [i];
cached_heap_top = hb->array + hb->firstfree;
cached_heap_end = hb->array + hb->size;
ret = cached_heap_top;
cached_heap_top += elem;
return ret;
}
void *getmem (object_descriptor _, size_t bytes)
{
void *ret;
if (bytes == 0)
bytes = _->size;
assert (bytes > 0);
/* make sure to allocate enough space for storing broken hearts */
if (bytes < sizeof (struct broken_heart))
bytes = sizeof (struct broken_heart);
ret = getbytes (bytes);
/* set everything to zero */
memset (ret, 0, bytes);
/* insert object_descriptor */
((struct broken_heart *)ret)->_ = _;
return ret;
}
/*
* Implementation of dump and restore
*/
static void recur_dump_ul (unsigned long l, FILE *file)
{
if (l != 0) {
recur_dump_ul (l / 0400, file);
putc (l % 0400, file);
}
}
void dump_ul (unsigned long l, FILE *file)
{
int i;
unsigned long tmp;
for (tmp = l, i = 0; tmp != 0; tmp /= 0400, i++);
putc (i, file);
recur_dump_ul (l, file);
}
unsigned long restore_ul (FILE *file)
{
int i, tmp;
unsigned long l;
if ((i = getc (file)) == EOF)
eof:
fatal ("bad dump file format");
l = 0;
while (i-- > 0) {
if ((tmp = getc (file)) == EOF)
goto eof;
l = l * 0400 + (tmp & 0377);
}
return l;
}
# define HASH_SIZE 37
/* In fact, that's not tunable, because portability of dumps depends on it */
# define refer_tag(x) ((unsigned long)(x))
# define hash_tag(x) (refer_tag(x)%HASH_SIZE)
static
unsigned long again_count[HASH_SIZE];
static
struct loctab_entry {
unsigned long tag;
void *location;
} *location_table[HASH_SIZE];
/*ARGSUSED*/
static
void do_struct_analysis (void **object_ptr, void *ignore)
{
struct broken_heart *obj = *object_ptr;
object_descriptor current;
if (obj == NULL)
return;
current = obj->_;
if (current->size == 0 && current->size_hook == NULL)
return;
switch (current->kind) {
case OD_NORMAL:
obj->_ = ¤t->whole_vector[OD_MARKED];
if (current->for_each_sub != NULL)
(* current->for_each_sub) (obj, do_struct_analysis, NULL);
break;
case OD_MARKED:
obj->_ = ¤t->whole_vector[OD_AGAIN];
++again_count[hash_tag(obj)];
break;
}
}
static
void structure_analysis (void)
{
int i;
perform_on_subs_proc fesp;
for (i = 0; i < gva_firstfree; i++)
if ((fesp = gva [i].for_each_sub) != NULL)
(* fesp) (gva [i].location, do_struct_analysis, NULL);
else
do_struct_analysis (gva [i].location, NULL);
}
/*ARGSUSED*/
static
void do_cleanup (void **object_ptr, void *ignore)
{
struct broken_heart *obj = *object_ptr;
object_descriptor current;
if (obj == NULL)
return;
current = obj->_;
if (current->kind != OD_NORMAL) {
obj->_ = ¤t->whole_vector[OD_NORMAL];
if (current->for_each_sub != NULL)
(* current->for_each_sub) (obj, do_cleanup, NULL);
}
}
static
void cleanup (void)
{
int i;
perform_on_subs_proc fesp;
for (i = 0; i < gva_firstfree; i++)
if ((fesp = gva [i].for_each_sub) != NULL)
(* fesp) (gva [i].location, do_cleanup, NULL);
else
do_cleanup (gva [i].location, NULL);
}
/*ARGSUSED*/
static
void object_dump (void **object_ptr, void *vfile)
{
struct broken_heart *obj = *object_ptr;
FILE *file = (FILE *) vfile;
object_descriptor od;
perform_on_subs_proc fesp;
if (obj == NULL) {
putc (NULL_IDENTIFIER, file);
return;
}
od = obj->_;
switch (od->kind) {
case OD_AGAIN:
obj->_ = &od->whole_vector[OD_WRITTEN];
putc (MARK_IDENTIFIER, file);
dump_ul (refer_tag(obj), file);
/* here is flow between cases, this is not an error! */
case OD_MARKED:
putc (od->identifier, file);
if (od->dump != NULL)
(* od->dump) (obj, file);
if ((fesp = od->for_each_sub) != NULL)
(* fesp) (obj, object_dump, vfile);
break;
case OD_NORMAL:
assert (od->size == 0 && od->size_hook == 0);
putc (od->identifier, file);
if (od->dump != NULL)
(* od->dump) (obj, file);
break;
default: /* OD_WRITTEN */
putc (REFERTO_IDENTIFIER, file);
dump_ul (refer_tag(obj), file);
break;
}
}
static
void dump (FILE *file)
{
int i;
perform_on_subs_proc fesp;
for (i = 0; i < gva_firstfree; i++)
if ((fesp = gva [i].for_each_sub) != NULL)
(* fesp) (gva [i].location, object_dump, (void *)file);
else
object_dump (gva [i].location, (void *)file);
}
void dump_storage (FILE *file)
{
int i;
assert (sizeof (void *) <= sizeof (unsigned long));
for (i = 0; i < HASH_SIZE; i++)
again_count[i] = 0;
structure_analysis ();
for (i = 0; i < HASH_SIZE; i++)
dump_ul (again_count[i], file);
dump (file);
cleanup ();
}
static
void init_location_table (FILE *file)
{
int i;
unsigned long tmp;
for (i = 0; i < HASH_SIZE; i++) {
again_count[i] = 0;
tmp = restore_ul (file);
if (tmp != 0)
if ((location_table[i] = (struct loctab_entry *)
malloc (tmp * sizeof (struct loctab_entry))) == NULL)
fatal ("ran out ouf space");
}
}
static
void free_location_table (void)
{
int i;
for (i = 0; i < HASH_SIZE; i++)
free (location_table[i]);
}
static
void register_location (unsigned long tag, void *location)
{
int index;
struct loctab_entry *ep;
index = hash_tag (tag);
ep = location_table[index] + again_count[index]++;
ep->tag = tag;
ep->location = location;
}
static
void *lookup_location (unsigned long tag)
{
int index;
struct loctab_entry *ep, *last;
index = hash_tag (tag);
last = (ep = location_table[index]) + again_count[index];
while (ep < last)
if (ep->tag == tag)
return ep->location;
else
ep++;
fatal ("bad dump file format");
/*NOTREACHED*/
}
static
void restore_object (void **object_ptr, void *vfile)
{
FILE *file = (FILE *) vfile;
int identifier;
unsigned long tag = 0; /* to shut up the compiler */
int marked;
void *location;
object_descriptor od;
marked = 0;
switch (identifier = getc (file)) {
case EOF:
eof:
fatal ("bad dump file format");
break;
case NULL_IDENTIFIER:
*object_ptr = NULL;
break;
case REFERTO_IDENTIFIER:
tag = restore_ul (file);
*object_ptr = lookup_location (tag);
break;
case MARK_IDENTIFIER:
marked = 1;
tag = restore_ul (file);
if ((identifier = getc (file)) == EOF)
goto eof;
default:
od = identifier_map[identifier];
if (od->restore_init == NULL) {
assert (od->size > 0);
location = new (od);
} else {
location = (*od->restore_init) (file);
assert (((struct broken_heart *)location)->_ == od);
}
*object_ptr = location;
if (marked)
register_location (tag, location);
if (od->for_each_sub != NULL)
(*od->for_each_sub) (location, restore_object, vfile);
if (od->restore_finish != NULL)
(*od->restore_finish) (location);
break;
}
}
void restore_storage (FILE *file)
{
int i;
perform_on_subs_proc fesp;
/* prevent gc from running (otherwise location table gets corrupted) */
gc_running = 1;
/* use passive heap */
exchange_heaps ();
init_location_table (file);
for (i = 0; i < gva_firstfree; i++)
if ((fesp = gva [i].for_each_sub) != NULL)
(* fesp) (gva [i].location, restore_object, (void *)file);
else
restore_object (gva [i].location, (void *)file);
free_location_table ();
/* reset gc_running flag */
gc_running = 0;
}
void *new_location_of (void *obj)
{
if (((struct broken_heart *) obj)->_ == &broken_heart_description)
return ((struct broken_heart *) obj)->new_location;
return obj;
}
/*
* Module initialization
*/
void init_all_modules (void)
{
all_modules (MODULE_INIT);
}